package top.fwpsl.jvm;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author: 风戏fw
 * @date: 2022/8/18
 * @description: 反射功能梳理
 */
public class ReflectionUtil {

    /**
     * 获取Class对象
     * 1. forName(className): 未知类和对象的时候使用, 执行会导致类加载, 类加载时静态代码块会执行
     * 2. 类名.class: 已知类的时候使用, 需要引入(import)
     * 3. 对象.getClass(): 已知对象的时候使用, 对实例化对象反射
     *
     * @param className
     * @return
     */
    public static Class<?> getClassByName(String className) {
        try {
            return Class.forName(className);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 实例化对象, 通过类名(带包名)
     *
     * @param className
     * @param <T>
     * @return
     */
    public static <T> T newInstance(String className) {
        return (T) newInstance(getClassByName(className));
    }

    /**
     * 实例化对象, 反射无参对象
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T newInstance(Class<T> clazz) {
        if (clazz == null) {
            return null;
        }

        try {
            return clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 实例化对象, 通过类名(带包名), 以及参数列表
     *
     * @param className
     * @param args
     * @param <T>
     * @return
     */
    public static <T> T newInstance(String className, Object... args) {
        return (T) newInstance(getClassByName(className), args);
    }

    /**
     * 实例化对象, 通过Class类以及参数列表
     *
     * @param clazz
     * @param args
     * @param <T>
     * @return
     */
    public static <T> T newInstance(Class<T> clazz, Object... args) {
        Class<?>[] clsArr = new Class[args.length];
        int i = 0;
        for (Object arg : args) {
            if (arg == null) {
                return null;
            }
            clsArr[i++] = arg.getClass();
        }

        return newInstance(clazz, clsArr, args);
    }

    /**
     * 实例化对象
     *
     * @param clazz
     * @param clsArr
     * @param args
     * @param <T>
     * @return
     */
    public static <T> T newInstance(Class<T> clazz, Class<?>[] clsArr, Object... args) {
        if (clazz == null) {
            return null;
        }

        Constructor constructor = null;
        try {
            constructor = clazz.getConstructor(clsArr);
            return (T) constructor.newInstance(args);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 根据变量名name获取指定的成员的值
     *
     * @param clazz
     * @param fieldName
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> Object getField(Class<T> clazz, String fieldName, T obj) {
        Field field = getField(clazz, fieldName);
        if (field == null) {
            return null;
        }

        try {
            field.setAccessible(true);
            return field.get(obj);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 指定类型的注解是否存在于此field上
     *
     * @param clazz
     * @param fieldName
     * @param annotation
     * @return
     */
    public static boolean isFieldAnnotationPresent(Class<?> clazz, String fieldName,
                                                   Class<? extends Annotation> annotation) {
        Field field = getField(clazz, fieldName);
        if (field == null) {
            return false;
        }


        return field.isAnnotationPresent(annotation);
    }

    /**
     * 根据变量名name获取指定的Field对象
     *
     * @param clazz
     * @param fieldName
     * @param <T>
     * @return
     */
    public static <T> Field getField(Class<T> clazz, String fieldName) {
        if (clazz == null) {
            return null;
        }

        try {
            return clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        return null;
    }


    /**
     * 根据方法名methodName与参数类型列表 调用指定方法
     *
     * @param clazz
     * @param methodName
     * @param parameteres 不能包含null
     * @param <T>
     * @return
     */
    public static <T> Object callMethod(Class<T> clazz, String methodName, T obj, Object... parameteres) {
        Class<?>[] parameterTypes = new Class[parameteres.length];
        int i = 0;
        for (Object p : parameteres) {
            if (p == null) {
                return null;
            }
            parameterTypes[i++] = p.getClass();
        }

        return callMethod(clazz, methodName, obj, parameterTypes, parameteres);
    }


    /**
     * 根据方法名methodName与参数类型列表 调用指定方法
     *
     * @param clazz
     * @param methodName
     * @param parameterTypes
     * @param <T>
     * @return
     */
    public static <T> Object callMethod(Class<T> clazz, String methodName, T obj,
                                       Class<?>[] parameterTypes, Object[] parameteres) {
        Method method = getMethod(clazz, methodName, parameterTypes);
        if (method == null) {
            return null;
        }

        try {
            return method.invoke(obj, parameteres);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 指定类型的注解是否存在于此方法上
     *
     * @param clazz
     * @param methodName
     * @param parameterTypes
     * @param annotation
     * @return
     */
    public static boolean isMethodAnnotationPresent(Class<?> clazz, String methodName, Class<?>[] parameterTypes,
                                                    Class<? extends Annotation> annotation) {
        Method method = getMethod(clazz, methodName, parameterTypes);
        if (method == null) {
            return false;
        }

        return method.isAnnotationPresent(annotation);
    }

    /**
     * 获取方法参数列表的注解数组
     *
     * @param clazz
     * @param methodName
     * @param parameterTypes
     * @return
     */
    public static Annotation[][] getParameterAnnotations(Class<?> clazz, String methodName, Class<?>[] parameterTypes) {
        Method method = getMethod(clazz, methodName, parameterTypes);
        if (method == null) {
            return new Annotation[0][0];
        }

        return method.getParameterAnnotations();
    }


    /**
     * 根据方法名methodName与参数类型列表 获取指定的方法对象
     *
     * @param clazz
     * @param methodName
     * @param parameterTypes
     * @param <T>
     * @return
     */
    public static <T> Method getMethod(Class<T> clazz, String methodName, Class<?>[] parameterTypes) {
        if (clazz == null) {
            return null;
        }

        try {
            return clazz.getDeclaredMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return null;
    }

}
